package com.zny.sescore.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.palmdoctor.mb.base.model.RuleFactor;
import com.palmdoctor.mb.base.rule.constant.ItemSourceEnum;
import com.zny.sescore.annotation.ItemSource;
import com.zny.sescore.annotation.SourceHandler;
import com.zny.sescore.dao.AssessModelDao;
import com.zny.sescore.exception.BizException;
import com.zny.sescore.exception.MessageEnum;
import com.zny.sescore.model.*;
import com.zny.sescore.model.vo.AssessModelListVO;
import com.zny.sescore.model.vo.AssessModelVO;
import com.zny.sescore.rule.constant.RelationOptEnum;
import com.zny.sescore.service.*;
import com.zny.sescore.util.JsonUtil;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 疾病评估模型service
 */
@Service
public class AssessModelServiceImpl extends ServiceImpl<AssessModelDao, AssessModel> implements AssessModelService {

    @Autowired
    private List<SourceHandler> sourceHandlerList;

    @Autowired
    private AssessModelItemService assessModelItemService;

    @Autowired
    private AssessModelOptionService assessModelOptionService;

    @Autowired
    private AssessModelRuleService assessModelRuleService;

    @Autowired
    private AssessModelResultService assessModelResultService;

    @Override
    public AssessModelVO getAssessModelVO(int modelId) {
        AssessModel model = getById(modelId);

        AssessModelItem modelQuery = new AssessModelItem();
        modelQuery.setModelId(modelId);
        List<AssessModelItem> itemList = assessModelItemService.list(new QueryWrapper<>(modelQuery));

        List<Integer> itemIdList = itemList.stream().map(AssessModelItem::getItemId).collect(Collectors.toList());
        List<AssessModelOption> optionList = new ArrayList<>();
        List<AssessModelRule> ruleList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(itemIdList)) {
            QueryWrapper<AssessModelOption> optionWrapper = new QueryWrapper<>();
            optionWrapper.in("item_id", itemIdList);
            optionList = assessModelOptionService.list(optionWrapper);

            QueryWrapper<AssessModelRule> ruleWrapper = new QueryWrapper<>();
            ruleWrapper.in("item_id", itemIdList);
            ruleList = assessModelRuleService.list(ruleWrapper);
        }

        QueryWrapper<AssessModelResult> resultWrapper = new QueryWrapper<>();
        resultWrapper.eq("model_id", model.getModelId());
        resultWrapper.orderByAsc("default_result");
        List<AssessModelResult> resultList = assessModelResultService.list(resultWrapper);
        return buildAssessModelVO(model, itemList, optionList, ruleList, resultList);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveAssessModel(AssessModelVO modelVO) {

        if (modelVO.getModelId() != null) {
            deleteItemOptionResult(modelVO.getModelId());
        }

        this.saveOrUpdate(modelVO);

        modelVO.getItemList().forEach(itemVO -> {
            itemVO.setModelId(modelVO.getModelId());
            assessModelItemService.save(itemVO);
            itemVO.getOptionList().forEach(opt -> {
                opt.setItemId(itemVO.getItemId());
            });
            assessModelOptionService.saveBatch(itemVO.getOptionList());

            itemVO.getRuleList().forEach(rule -> {
                rule.setItemId(itemVO.getItemId());
                rule.setScoreExp(buildRuleExp(rule.getExpList()));
                rule.setScoreExpJson(JsonUtil.objectToString(rule.getExpList()));
            });
            assessModelRuleService.saveBatch(itemVO.getRuleList());
        });

        modelVO.getResultList().forEach(result -> {
            result.setModelId(modelVO.getModelId());
            result.setResultExp(buildRuleExp(result.getExpList()));
            result.setResultExpJson(JsonUtil.objectToString(result.getExpList()));
        });
        AssessModelResult resultQuery = new AssessModelResult();
        resultQuery.setModelId(modelVO.getModelId());
        assessModelResultService.remove(new QueryWrapper<>(resultQuery));
        assessModelResultService.saveBatch(modelVO.getResultList());
        return true;
    }

    @Override
    public List<CodeName> getSourceCategory1(String itemSource) {
        return getSourceHandler(ItemSourceEnum.getByCode(itemSource)).getSourceCategory1();
    }

    @Override
    public List<CodeName> getSourceCategory2(String itemSource, String parentCode) {
        return getSourceHandler(ItemSourceEnum.getByCode(itemSource)).getSourceCategory2(parentCode);
    }

    @Override
    public IPage<AssessModelListVO> getModelPage(Page<AssessModel> page, QueryWrapper<AssessModel> queryWrapper) {
        return baseMapper.selectModelPage(page, queryWrapper.getEntity());
    }

    private AssessModelVO buildAssessModelVO(AssessModel model, List<AssessModelItem> itemList, List<AssessModelOption> optionList
            , List<AssessModelRule> ruleList, List<AssessModelResult> resultList) {
        AssessModelVO modelVO = new AssessModelVO();
        BeanUtils.copyProperties(model, modelVO);
        ruleList.forEach(rule -> {
            if (StringUtils.isNotEmpty(rule.getScoreExpJson())) {
                rule.setExpList(JSONArray.parseArray(rule.getScoreExpJson(), RuleFactor.class));
            }
        });
        resultList.forEach(result -> {
            if (StringUtils.isNotEmpty(result.getResultExpJson())) {
                result.setExpList(JSONArray.parseArray(result.getResultExpJson(), RuleFactor.class));
            }
        });
        modelVO.setItemList(
                itemList.stream().map(item -> {
                    AssessModelVO.AssessModelItemVO itemVO = new AssessModelVO.AssessModelItemVO();
                    BeanUtils.copyProperties(item, itemVO);
                    itemVO.setOptionList(
                            optionList.stream()
                                    .filter(option -> Objects.equals(option.getItemId(), item.getItemId()))
                                    .collect(Collectors.toList())
                    );

                    itemVO.setRuleList(
                            ruleList.stream()
                                    .filter(rule -> Objects.equals(rule.getItemId(), item.getItemId()))
                                    .collect(Collectors.toList())
                    );
                    return itemVO;
                }).collect(Collectors.toList())
        );

        modelVO.setResultList(resultList);
        return modelVO;
    }

    private SourceHandler getSourceHandler(ItemSourceEnum sourceEnum) {
        return sourceHandlerList.stream()
                .filter(handler -> handler.getClass().getAnnotation(ItemSource.class).value() == sourceEnum)
                .findFirst()
                .orElseThrow(() -> new BizException(MessageEnum.ASSESS_CONFIG_ERROR));
    }

    private void deleteItemOptionResult(Integer modelId) {
        AssessModelItem modelQuery = new AssessModelItem();
        modelQuery.setModelId(modelId);
        List<AssessModelItem> itemList = assessModelItemService.list(new QueryWrapper<>(modelQuery));
        List<Integer> itemIdList = itemList.stream().map(AssessModelItem::getItemId).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(itemIdList)) {
            assessModelItemService.removeByIds(itemIdList);
            QueryWrapper<AssessModelOption> optionWrapper = new QueryWrapper<>();
            optionWrapper.in("item_id", itemIdList);
            assessModelOptionService.remove(optionWrapper);

            QueryWrapper<AssessModelRule> ruleWrapper = new QueryWrapper<>();
            ruleWrapper.in("item_id", itemIdList);
            assessModelRuleService.remove(ruleWrapper);
        }

        QueryWrapper<AssessModelResult> resultWrapper = new QueryWrapper<>();
        resultWrapper.in("model_id", modelId);
        assessModelResultService.remove(resultWrapper);
    }


    private String buildRuleExp(List<RuleFactor> expList) {
        StringBuilder builder = new StringBuilder();
        for (RuleFactor factor : expList) {

            builder.append(" ").append(factor.getLogicalOpt()).append(" (");
            if (factor.getValue1() != null) {
                builder.append(factor.getValue1());
            }
            if (factor.getRelationOpt() != null) {
                builder.append(factor.getRelationOpt());
            }
            // 关系运算符include/exclude需要加() 例如a.include(b)
            RelationOptEnum relationOptEnum = RelationOptEnum.getByCode(factor.getRelationOpt());
            boolean relationOptBrackets = relationOptEnum != null && relationOptEnum.isBrackets();
            if (relationOptBrackets) {
                builder.append("('");
            }
            if (factor.getValue2() != null) {
                builder.append(factor.getValue2());
            }
            if (relationOptBrackets) {
                builder.append("')");
            }

            builder.append(")");
        }
        return builder.toString();
    }
}




